package com.hyjiacan.tools.http.handlers

import com.hyjiacan.tools.http.models.RequestContext
import com.hyjiacan.tools.http.SessionManager
import com.hyjiacan.tools.http.exceptions.HTTPException
import com.hyjiacan.tools.utils.Json
import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpHandler
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.zip.GZIPOutputStream

abstract class BaseHandler(val sessionEnabled: Boolean) : HttpHandler {

    override fun handle(exchange: HttpExchange) {
        val request = RequestContext(exchange, sessionEnabled)

        var status: Int

        var result: Any
        try {
            result = dispatch(request)
            status = 200
        } catch (e: HTTPException) {
            status = e.status
            result = ""
        } catch (e: Exception) {
            status = 500
            e.printStackTrace()
            result = "${e.message}\n${e.printStackTrace()}"
        }

        val contentType: String

        var buffer = when (result) {
            is String -> {
                contentType = "text/plain; charset=utf-8"
                result.toByteArray()
            }

            is File -> {
                contentType = "application/octet-stream"
                exchange.responseHeaders.set("Content-Disposition", "attachment;filename=${result.name}")
                result.readBytes()
            }

            else -> {
                contentType = "application/json; charset=utf-8"
                Json.stringify(result).toByteArray()
            }
        }
        if (exchange.requestHeaders.containsKey("accept-encoding") && exchange.requestHeaders.getFirst("accept-encoding")
                .contains("gzip")
        ) {
            buffer = compress(buffer)
            exchange.responseHeaders.set("Content-Encoding", "gzip")
        }
        if (!exchange.responseHeaders.containsKey("Content-Type")) {
            request.exchange.responseHeaders.set("Content-Type", contentType)
        }

        if (sessionEnabled) {
            if (
                !contentType.startsWith("text/css") &&
                !contentType.startsWith("text/javascript") &&
                !contentType.startsWith("image/")
            ) {
                // 设置 cookie
                exchange.responseHeaders.set(
                    "Set-Cookie",
                    "${SessionManager.COOKIE_NAME}=${request.session!!.sessionId};Max-Age=${SessionManager.EXPIRE_TIME};Path=/;HttpOnly"
                )
            }
        }

        exchange.sendResponseHeaders(status, buffer.size.toLong())
        val os = exchange.responseBody
        os.write(buffer)
        os.close()
    }

    private fun dispatch(request: RequestContext): Any {

        when (request.method) {
            RequestContext.GET -> {
                return get(request)
            }

            RequestContext.POST -> {
                return post(request)
            }

            RequestContext.PUT -> {
                return put(request)
            }

            RequestContext.DELETE -> {
                return delete(request)
            }

            else -> {
                throw Exception("Invalid request method")
            }
        }

    }

    private fun compress(byteArray: ByteArray): ByteArray {
        val outputStream = ByteArrayOutputStream()
        val gzipStream = GZIPOutputStream(outputStream)
        gzipStream.write(byteArray)
        gzipStream.finish()
        gzipStream.close()
        val data = outputStream.toByteArray()
        outputStream.close()
        return data
    }

    abstract fun get(request: RequestContext): Any
    abstract fun post(request: RequestContext): Any
    abstract fun put(request: RequestContext): Any
    abstract fun delete(request: RequestContext): Any
}